<?php
/***
 * Candy框架 框架调试模式类 和 Exception处理类
 * 
 * $Author: 刘森 (fingerboy@qq.com) $
 * $Date: 2019-07-25 01:56:31 $   
 */

declare (strict_types = 1);
namespace Candy\Core;
use \Workerman\Protocols\{Http,HttpCache};

defined('CANDY') OR die('You Are A Bad Guy. o_O???');

final Class Debug {
	public static $includefile = [];
	public static $application = [];
	public static $info = [];
	public static $sqls = [];
	public static $paths = [];
	public static $errors = [];
	public static $startTime;                //保存脚本开始执行时的时间（以微秒的形式保存）
	
	private static $msg = [
			E_WARNING=>'运行时警告',
			E_NOTICE=>'运行时提醒',
			E_STRICT=>'编码标准化警告',
			E_USER_ERROR=>'自定义错误',
			E_USER_WARNING=>'自定义警告',
			E_USER_NOTICE=>'自定义提醒',
			'Unkown'=>'未知错误'
		];
	
	/**
	 * 在脚本开始处调用获取脚本开始时间的微秒值
	 */
	public static function start(): void
	{   
		//自定义异常处理
		set_error_handler(['Candy\Core\Debug', 'catcher']);
		set_exception_handler(['Candy\Core\Debug', 'exception']);
		register_shutdown_function(['Candy\Core\Debug', 'shutdown']);	
		if(isDebug()){
			error_reporting(E_ALL);
		}else{
			error_reporting(E_ALL^E_WARNING^E_NOTICE);
		}
		ini_set('display_errors', 'Off'); 		//屏蔽错误输出
		
		//记录脚本开始时间
		Debug::$startTime = microtime(true);
	}
	
	/**
	 *初始化参数
	 */
	public static function reload(): void
	{
		self::$startTime = microtime(true);
		self::$includefile = [];
		self::$application = [];
		self::$info = [];
		self::$sqls = [];
		self::$paths = [];
		self::$errors = [];
		
		if(self::$sqls){
			$dbEngine = 'Candy\\Extend\\DB\\Driver\\' . ucfirst(strtolower(G('DRIVER')));
			$dbEngine::$execute = 0;
			$dbEngine::$totalcate = 0;
		}
	}

	/**
	 *返回同一脚本中两次获取时间的差值
	 */
	public static function spent(): float
	{
		return round((microtime(true) - self::$startTime) * 1000, 2);  //计算后以4舍5入保留2位返回 ms
	}
	
	/**
	 *错误 handler
	 */
	public static function catcher(string $errno,string $errstr,string $errfile,int $errline): void
	{
   		if(!isset(self::$msg[$errno])) 
			$errno='Unkown';
		
		if(stripos($errstr, 'open_basedir restriction in effect.') === false)
			//忽略注意级别的提示
			if(!($errno == E_NOTICE || $errno == E_USER_NOTICE)){
				if(isDebug()){
					$mess='<font color="red">';
					$mess.='<b>'. self::$msg[$errno] ."</b>[在文件 {$errfile} 中,第 $errline 行]:";
					$mess.=$errstr;
					$mess.='</font>'; 
					self::addMsg($mess, 4);
				}else{
					//不记录 stream_select() 错误
					if(stripos($errstr, 'stream_select():') === false){
						$message=sprintf("[%s]:%s in %s on line %d", self::$msg[$errno], trim($errstr),  $errfile, $errline);
						Log::input($message, -1, LOGPATH . 'error.log');
					}
				}
			}
	}
	
	/**
     * 异常处理函数
     * 
     * @param $e \Exception
     */
    public static function exception($e): void
    {
		$detailed = '<font color="red"><b>Exception error</b>[在文件 '.$e->getFile().' 中,第 '.$e->getLine().' 行]:'.$e->getMessage().'</font>';
		self::showMessage($e->getMessage(), 'Exception', $detailed);
    }
	
	/**
     * 关闭处理函数
     */
	public static function shutdown(): void
    {
        if (!is_null($error = error_get_last())){
			$detailed='<font color="red"><b>Shutdown error</b>[在文件 '. $error['file'] .' 中,第 '. $error['line'] .' 行]:'.$error['message'].'</font>';
			self::showMessage($error['message'], 'Shutdown', $detailed);
        }
    }
	
	/**
	 * 添加调试消息
	 *
	 * @param	string	$msg	调试消息字符串
	 * @param	int	$type	消息的类型
	 */
	public static function addMsg(string $msg,int $type=0): void
	{
		if(isDebug()){
			switch($type){
				case 0:
					self::$info[]=$msg;
					break;
				case 1:
					self::$includefile['app'][] = $msg;
					break;
				case 2:
					self::$sqls[]=$msg;
					break;
				case 3:
					self::$paths[]=$msg;
					break;
				case 4:
					self::$errors[]=$msg;
					break;
				case 5:
					self::$includefile['framework'][] = $msg;
					break;
				case 6:
					self::$application[] = $msg;
					break;
			}
		}
	}
	
	/**
	 * 输出调试消息
	 */
	public static function message(): void
	{
		showTplMsg();
	}
	
	/**
     * 展示错误
	 *
	 * @param	string	$msg	调试消息字符串
	 * @param	string	$type	消息的类型
	 * @param	string	$detailed	扩展消息
     */
	public static function showMessage(string $msg,string $type = '',string $detailed = ''): void
    {
		$msg = mConvtEncod($msg);
		if(defined('CLIWORKING')){
			echo $msg;
		}else{
			if(is_null(C('API'))){
				showTplMsg('shutdown', $msg, $detailed, $type);
			}else{
				//补充sessionid
				$data['status'] = 0;
				$data['msg'] = $detailed ?: $msg;
				$data['sessionid'] = session_id();
				if(C('API') == 'json'){
					echo json($data);
				}else{
					Xml::arraytoXml($data);
				}
			}
			stop();
		}
    }

}
